package com.semanticweb.webapp.ontology;

import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.sdb.store.DatabaseType;
import com.semanticweb.project.api.Ontology;
import com.semanticweb.project.api.OntologyFactory;

/**
 * Class allows to manipulate with CIDOC Ontology. <br>
 * Resources in Ontology are identified by URI. <br>
 * URI is consisted of namespace and suffix. By convetion, namespace ends with {@link #NS_SEPARATOR} sign. <br>
 * <br>
 * Example: http://www.ftn.uns.ac.rs/sw/museum#123 <br>
 * Namespace is: http://www.ftn.uns.ac.rs/sw/museum# <br>
 * suffix is: 123
 * 
 * @author Sholy
 * 
 */
public class Ont {

    public static String JDBC_URL = OntBundle.getString("JDBC_URL");
    public static String JDBC_NAME = OntBundle.getString("JDBC_NAME");
    public static String JDBC_PWD = OntBundle.getString("JDBC_PWD");

    public static String ONT_FILE_URL = OntBundle.getString("ONT_FILE_URL");

    public static String NS = OntBundle.getString("NS");
    public static String NS_KEY = "m";

    public static String RDFS_NS = "http://www.w3.org/2000/01/rdf-schema#";
    public static String RDFS_KEY = "rdfs";

    public static String RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
    public static String RDF_KEY = "rdf";

    public static DatabaseType DB_TYPE = DatabaseType.fetch(OntBundle.getString("DB_TYPE"));
    public static String DB_DRIVER = OntBundle.getString("DB_DRIVER");

    public static String URI_SEPARATOR = ".";
    public static String NS_SEPARATOR = "#";

    public static String QUERY_PREFIX = "PREFIX";
    public static String QUERY_PREFIX_VALUE_KEY = ":";
    public static String QUERY_PREXIF_OPEN_KEY = "<";
    public static String QUERY_PREFIX_CLOSE_KEY = ">";

    private static Ontology ont; // TODO: initialize on server start-up

    public static Ontology getOntology() {
        return ont;
    }

    public static void initializeOnt() {
        if (ont == null) {
            ont = OntologyFactory.createOntologyImpl(JDBC_URL, JDBC_NAME, JDBC_PWD, DB_TYPE, ONT_FILE_URL, NS,
                    DB_DRIVER);
        }
    }

    /**
     * Formats URI suffix from uriParts. <br>
     * Method accepts parts of URI, it will concatenate them using {@link #URI_SEPARATOR}. <br>
     * 
     * @param uriParts
     *            - parts of URI
     * @return - suffix of URI
     */
    public static String formatURIPart(final String... uriParts) {
        final StringBuffer retVal = new StringBuffer();
        boolean first = true;
        for (final String s : uriParts) {
            if (!first) {
                retVal.append(URI_SEPARATOR);
            } else {
                first = false;
            }
            retVal.append(s);
        }

        return retVal.toString();
    }

    /**
     * Formats URI of the resource. <br>
     * Method accepts parts of URI, it will concatenate them using {@link #URI_SEPARATOR}. <br>
     * Method will return full URI - meaning it will prefix URI parts with namespace.
     * 
     * @param uriParts
     *            - parts of URI
     * @return - full URI String
     */
    public static String formatURI(final String... uriParts) {
        final StringBuffer retVal = new StringBuffer(NS);
        retVal.append(formatURIPart(uriParts));

        return retVal.toString();
    }

    /**
     * Formats URI for SPARQL query use. <br>
     * Method accepts parts of URI, it will concatenate them using {@link #URI_SEPARATOR}. <br>
     * Method will return URI acceptable to SPARQL - meaning it will prefix URI parts with {@link #NS_KEY}.
     * 
     * @param uriParts
     *            - parts of URI
     * @return - URI string for SPARQL query.
     */
    public static String formatQueryURI(final String... uriParts) {
        final StringBuffer retVal = new StringBuffer();
        retVal.append(NS_KEY);
        retVal.append(QUERY_PREFIX_VALUE_KEY);
        retVal.append(formatURIPart(uriParts));

        return retVal.toString();
    }

    /**
     * Returns namespace of URI.
     * 
     * @param URI
     *            - full URI, containing namespace and suffix
     * @return - namepsace part of URI
     */
    public static String isolateURINamespace(final String URI) {
        final int index = URI.indexOf(NS_SEPARATOR);
        if (index == -1) {
            return null;
        }
        return URI.substring(0, index);
    }

    /**
     * Returns URI suffix.
     * 
     * @param URI
     *            - full URI, containing namespace and suffix
     * @return - URI suffix
     */
    public static String isolateURISuffix(final String URI) {
        final int index = URI.indexOf(NS_SEPARATOR);
        if (index == -1) {
            return null;
        }
        return URI.substring(index + 1);
    }

    /**
     * Formats prefix for SPARQL query.
     * 
     * @param key
     *            - prefix key
     * @param value
     *            - prefix value (namespace)
     * @return - SPARQL query prefix
     */
    public static String formatQueryPrefix(final String key, final String value) {
        final StringBuffer str = new StringBuffer();
        str.append(QUERY_PREFIX);
        str.append(" ");
        str.append(key);
        str.append(QUERY_PREFIX_VALUE_KEY);
        str.append(" ");
        str.append(QUERY_PREXIF_OPEN_KEY);
        str.append(value);
        str.append(QUERY_PREFIX_CLOSE_KEY);
        str.append(" ");
        return str.toString();
    }

    /**
     * Creates default SPARQL prefixes for CIDOC ontology.
     * 
     * @return
     */
    public static String defaultQueryPrefixes() {
        final StringBuffer prefixes = new StringBuffer();
        prefixes.append(formatQueryPrefix(NS_KEY, NS));
        prefixes.append(formatQueryPrefix(RDFS_KEY, RDFS_NS));
        prefixes.append(formatQueryPrefix(RDF_KEY, RDF_NS));

        return prefixes.toString();
    }

    /**
     * Executes SPARQL SELECT query using default CIDOC prefixes formed by {@link #defaultQueryPrefixes()} method.
     * 
     * @param query
     *            - SPARQL SELECT query
     * @return - ResultSet of query
     */
    public static ResultSet quickQuery(final String query) {
        final StringBuffer queryStr = new StringBuffer();
        queryStr.append(defaultQueryPrefixes());

        queryStr.append(query);

        return getOntology().query(queryStr.toString());
    }

    /**
     * Executes SPARQL SELECT query using default CIDOC prefixes formed by {@link #defaultQueryPrefixes()} method. <br>
     * Method returns values of desirable variable specified in item parameter.
     * 
     * @param query
     *            - SPARQL SELECT query
     * @param item
     *            - query item to return
     * @return Array of values representing result of query
     */
    public static String[] quickQueryStr(final String query, final String item) {
        final StringBuffer queryStr = new StringBuffer();
        queryStr.append(defaultQueryPrefixes());

        queryStr.append(query);

        return getOntology().queryStr(queryStr.toString(), item);
    }

    /**
     * Executes SPARQL ASK query using default CIDOC prefixes formed by {@link #defaultQueryPrefixes()} method. <br>
     * Method returns boolean value as result of query.
     * 
     * @param query
     *            - SPARQL ASK query
     * @return - existence of value questioned via query
     */
    public static boolean quickAskQuery(final String query) {
        final StringBuffer queryStr = new StringBuffer();
        queryStr.append(defaultQueryPrefixes());

        queryStr.append(query);

        return getOntology().askQuery(queryStr.toString());
    }

    /**
     * Creates Ontology Individual of {@link CIDOC#E55} type and adds label specified in label parameter.
     * 
     * @param typeURI
     *            - desirable URI of object to be created
     * @param label
     *            - label of type
     */
    public static void createType(final String typeURI, final String label) {
        getOntology().createIndividual(CIDOC.E55.getFullValue(), typeURI);
        getOntology().addLabel(typeURI, label);
    }

    /**
     * Checks if object of {@link CIDOC#E55} type with label typeValue exists.
     * 
     * @param typeValue
     *            - value (label) of type object
     * @return - existence status
     */
    public static boolean typeValueExists(final String typeValue) {
        final String format = "ASK WHERE { ?type rdf:type %s; rdfs:label ?label. FILTER (?label = \"%s\") }";
        final String query = String.format(format, CIDOC.E55.queryValue(), typeValue);

        return quickAskQuery(query);
    }

    /**
     * Checks if object of {@link CIDOC#E55} type with URI suffix typeSuffixURI exists.
     * 
     * @param typeRelativeURI
     *            - URI suffix of type object
     * @return - existence status
     */
    public static boolean typeURIExists(final String typeSuffixURI) {
        final String format = "ASK WHERE { %s rdf:type %s }";
        final String query = String.format(format, formatQueryURI(typeSuffixURI), CIDOC.E55.queryValue());

        return quickAskQuery(query);
    }

    public static void typeCheck(final CType type) {
        if (!typeValueExists(type.getValue())) {
            createType(type.getURI(), type.getValue());
        }
    }
}
